using System;
using System.Collections.Generic;
using CoreLibrary.Messaging.Sql;
using CoreLibrary.Sql.Linq;

namespace CoreLibrary.Messaging.Store {

    /// <summary>
    /// Database table can be created with the query below:
    /// 
    /// CREATE TABLE [QUEUE_NAME](
    ///    [id] [uniqueidentifier] NOT NULL,
    ///    [aid] [int] NOT NULL,
    ///    [body] [varchar](max) NOT NULL,
    ///    [status] [int] NOT NULL,
    ///    [fault] [varchar](250) NOT NULL,
    ///    [retry] [int] NOT NULL,
    ///    [date_created] [datetime] NOT NULL,
    ///    [date_scheduled] [datetime] NOT NULL,
    ///    CONSTRAINT [PK_QueueName] PRIMARY KEY (
    ///       [id] ASC
    ///    )
    /// )
    /// </summary>
    public class SQLMessageStorage : AbstractLinqSqlService, IMessageStore {

        public string TableName { get; set; }

        protected override void OnInit() {
            base.OnInit();
            Asserts.NotBlank(TableName, "Table name is required.");
        }

        public bool RemoveMessage(string messageId) {
            return Execute(new DeleteMessage(TableName, messageId));
        }

        public int CountTotalMessage() {
            return Execute(new CountTotalMessage(TableName));
        }

        public int CountMessage(ListMessageQuery query) {
            return Execute(new CountMessage(TableName, query));
        }

        public int CountEnqueuedMessage() {
            return Execute(new CountMessage(TableName, new ListMessageQuery {
                MessageStatuses = new[] { (int)MessageStatus.Enqueued },
                ScheduledDateFrom = null, // search from the beginning
                ScheduledDateTo   = null // search up till now
            }));
        }

        public int CountProcessingMessage() {
            return Execute(new CountMessage(TableName, new ListMessageQuery {
                MessageStatuses   = new[] { (int)MessageStatus.Processing },
                ScheduledDateFrom = null, // search from the beginning
                ScheduledDateTo   = null // search up till now
            }));
        }

        public Message GetMessage(string messageId) {
            return Execute(new GetMessage(TableName, messageId));
        }

        public MessageStatus? GetMessageStatus(string messageId) {
            return Execute(new GetMessageStatus(TableName, messageId));
        }

        public List<Message> ListMessages(ListMessageQuery query) {
            return Execute(new ListMessages(TableName, query));
        }

        public void ListMessages(ListMessageQuery query, Action<Message> callback) {
            var result = ListMessages(query);
            foreach (var message in result) {
                callback(message);
            }
        }

        public void InsertMessage(Message message) {
            Execute(new InsertMessage(TableName, message));
        }

        public bool UpdateMessageStatus(string messageId, 
                                        MessageStatus newStatus, 
                                        string fault, 
                                        int retry, 
                                        DateTime dateScheduled) {
            return Execute(new UpdateMessageStatus(TableName, messageId, newStatus, fault, retry, dateScheduled));
        }

        public bool CompareAndUpdateMessageStatus(string messageId, 
                                                  MessageStatus expectedStatus, 
                                                  MessageStatus newStatus, 
                                                  string fault, 
                                                  int retry, 
                                                  DateTime dateScheduled) {
            return Execute(new CompareAndUpdateStatus(TableName, messageId, expectedStatus, newStatus, fault, retry, dateScheduled));
        }
        
    }

}